#ifndef __VANILA_BASE_OBJECT_HH__
#define __VANILA_BASE_OBJECT_HH__

#include "vanila/object.h"
#include <string>
#include <map>

namespace vanila
{
class ObjectString : public Object
{
public:
    //! \brief constructor and desctructor
    ObjectString(const char* chars, uint32_t length);
    ObjectString(std::string str);
    virtual ~ObjectString() noexcept override {}

    //! \brief print object string
    virtual void print(bool callStr = true) const override;

    //! \brief wheather this < that of string
    virtual bool less(const Object* that) const override;

    //! \brief wheather this > that of string
    virtual bool greater(const Object* that) const override;

    //! \brief add two object
    virtual Value add(const Object* that) const override;

private:
    std::string _str;

public:
    const char* cstr() const noexcept
    { return this->_str.c_str(); }
    
    char* cstr() noexcept
    { return const_cast<char*>(this->_str.c_str()); }

    std::string& str() noexcept
    { return this->_str; }
    
    const std::string& str() const noexcept
    { return this->_str; }
};

class ObjectFunction : public Object
{
public:
    //! \brief constructor and desctructor
    ObjectFunction();
    virtual ~ObjectFunction() noexcept override {};

    //! \brief print object function
    virtual void print(bool callStr = true) const override;

    //! \brief arity increase
    void increaseArity() noexcept
    { this->_arity++; }

    //! \brief upvalue increase
    void increaseUpvalueCount() noexcept
    { this->_upvalueCount++; }

private:
    uint32_t _arity;
    uint32_t _upvalueCount;
    Chunk _chunk;
    ObjectString* _name;

public:
    uint32_t arity() const noexcept
    { return this->_arity; }

    uint32_t upvalueCount() const noexcept
    { return this->_upvalueCount; }

    const Chunk& chunk() const noexcept
    { return this->_chunk; }

    Chunk& chunk() noexcept
    { return this->_chunk; }

    const ObjectString* name() const noexcept
    { return this->_name; }
    
    ObjectString* name() noexcept
    { return this->_name; }
    
    void setName(ObjectString* name) noexcept
    { this->_name = name; }
};

class ObjectClosure : public Object
{
public:
    //! \brief constructor and desctructor
    ObjectClosure(ObjectFunction* function) noexcept;
    virtual ~ObjectClosure() noexcept override {}

    //! \brief print object closure
    virtual void print(bool callStr = true) const override;

private:
    ObjectFunction* _function;
    std::vector<ObjectUpvalue*> _upvalues;

public:
    ObjectFunction* function() const noexcept
    { return this->_function; }

    const std::vector<ObjectUpvalue*>& upvalues() const noexcept
    { return this->_upvalues; }
    
    std::vector<ObjectUpvalue*>& upvalues() noexcept
    { return this->_upvalues; }
};

class ObjectUpvalue : public Object
{
public:
    //! \brief constructor and desctructor
    ObjectUpvalue(Value* slot) noexcept;
    virtual ~ObjectUpvalue() noexcept override {}

    //! \brief print object upvalue
    virtual void print(bool callStr = true) const override;

private:
    Value* _location;
    Value _closed;
    ObjectUpvalue* _next;

public:
    Value* location() const noexcept
    { return this->_location; }
    
    void setLocation(Value* slot) noexcept
    { this->_location = slot; }

    ObjectUpvalue* next() const noexcept
    { return this->_next; }
    
    void setNext(ObjectUpvalue* next) noexcept
    { this->_next = next; }

    Value& closed() noexcept
    { return this->_closed; }
    
    void setClosed(const Value& closed) noexcept
    { this->_closed = closed; }
};

class ObjectNative : public Object
{
public:
    //! \brief constructor and desctructor
    ObjectNative(NativeFunction function) noexcept;
    virtual ~ObjectNative() noexcept override {}

    //! \brief print object native
    virtual void print(bool callStr = true) const override;
    
    //! \brief execute the native c++ function
    Value execute(uint32_t argCount, Value* args) const;

private:
    NativeFunction _function;
};

class ObjectClass : public Object
{
public:
    static ObjectString* initMethodName;
    static ObjectString* strMethodName;
    static ObjectString* hashMethodName;
    static ObjectString* equalMethodName;
    static ObjectString* lessMethodName;
    static ObjectString* greaterMethodName;
    static ObjectString* addMethodName;
    static ObjectString* subMethodName;
    static ObjectString* mulMethodName;
    static ObjectString* divMethodName;

    static ObjectClass* list;
    static ObjectClass* dict;
    static ObjectClass* set;

public:
    //! \brief constructor and desctructor
    ObjectClass(ObjectString* name, bool isNative) noexcept;
    virtual ~ObjectClass() noexcept override {}

    //! \brief print object class
    virtual void print(bool callStr = true) const override;

public:
    //! \brief add a nameed method value
    void addMethod(ObjectString* name, const Value& method)
    { this->_methods[name] = method; }

    //! \brief add a nemed method(native function)
    void addMethod(ObjectString* name, NativeFunction native);

    //! \brief add a named method
    void addMethod(const char* name, NativeFunction native);

    //! \brief add s static field
    void addField(ObjectString* name, const Value& field)
    { this->_fields[name] = field; }

public:
    //! \brief add init method
    void addInitMethod(const Value& method)
    { this->_methods[ObjectClass::initMethodName] = method; }

    //! \brief add NativeFunction init method
    void addInitMethod(NativeFunction native);

    //! \brief add str method
    void addStrMethod(const Value& method)
    { this->_methods[ObjectClass::strMethodName] = method; }

    //! \brief add NativeFunction str method
    void addStrMethod(NativeFunction native);

public:
    //! \brief cehck wheather name in class
    bool findFiled(ObjectString* name);
    
    //! \brief find the filed in specify name
    bool findFiled(ObjectString* name, Value& filed);

    //! \brief find the method in specify name
    bool findMethod(ObjectString* name, Value& method);

    //! \brief find the '__init__' method
    //! \brief return false is no found
    bool findInitMethod(Value& method)
    { return this->findMethod(ObjectClass::initMethodName, method); }

    //! \brief find the '__str__' method
    bool findStrMethod(Value& method)
    { return this->findMethod(ObjectClass::strMethodName, method); }

    //! \brief find the '__hash__' method
    bool findHashMethod(Value& method)
    { return this->findMethod(ObjectClass::hashMethodName, method); }

    //! \brief find the '__equal__' method
    bool findEqualMethod(Value& method)
    { return this->findMethod(ObjectClass::equalMethodName, method); }

    //! \brief find the '__less__' method
    bool findLessMethod(Value& method)
    { return this->findMethod(ObjectClass::lessMethodName, method); }

    //! \brief find the '__greater__' method
    bool findGreaterMethod(Value& method)
    { return this->findMethod(ObjectClass::greaterMethodName, method); }

    //! \brief find the '__add__' method
    bool findAddMethod(Value& method)
    { return this->findMethod(ObjectClass::addMethodName, method); }

    //! \brief find the '__sub__' method
    bool findSubMethod(Value& method)
    { return this->findMethod(ObjectClass::subMethodName, method); }

    //! \brief find the '__mul__' method
    bool findMulMethod(Value& method)
    { return this->findMethod(ObjectClass::mulMethodName, method); }

    //! \brief find the '__div__' method
    bool findDivMethod(Value& method)
    { return this->findMethod(ObjectClass::divMethodName, method); }

private:
    const bool _isNative;
    ObjectString* _name;
    std::map<ObjectString*, Value> _methods;
    std::map<ObjectString*, Value> _fields;

public:
    const bool isNative() const noexcept
    { return this->_isNative; }

    ObjectString* name() const noexcept
    { return this->_name; }

    std::map<ObjectString*, Value>& methods() noexcept
    { return this->_methods; }
    
    const std::map<ObjectString*, Value>& methods() const noexcept
    { return this->_methods; }

    std::map<ObjectString*, Value>& fields() noexcept
    { return this->_fields; }
    
    const std::map<ObjectString*, Value>& fields() const noexcept
    { return this->_fields; }
};

class ObjectInstance: public Object
{
public:
    //! \brief constructor and desctructor
    ObjectInstance(ObjectClass* klass);
    virtual ~ObjectInstance() noexcept override;

    //! \brief find the specify filed in instance
    bool findField(ObjectString* name, Value& field);

public:
    //! \brief print object instance
    virtual void print(bool callStr = true) const override;

    //! \brief get object instance's hash value
    virtual int64_t hash() const override;

    //! \brief wheather this == that
    virtual bool equal(const Object* that) const override;

    //! \brief wheather this < that
    virtual bool less(const Object* that) const override;

    //! \brief wheather this > that
    virtual bool greater(const Object* that) const override;

    //! \brief add two object
    virtual Value add(const Object* that) const override;

    //! \brief sub two object
    virtual Value sub(const Object* that) const override;

    //! \brief mul two object
    virtual Value mul(const Object* that) const override;

    //! \brief div two object
    virtual Value div(const Object* that) const override;

private:
    ObjectClass* _klass;
    union
    {
        void* _value;
        Object* _object;
        std::map<ObjectString*, Value>* _fields;
    };

public:
    const bool isNative() const noexcept
    { return this->_klass->isNative(); }

    ObjectClass* klass() const noexcept
    { return this->_klass; }

    std::map<ObjectString*, Value>& fields() noexcept
    { return *this->_fields; }
    
    const std::map<ObjectString*, Value>& fields() const noexcept
    { return *this->_fields; }

    Object* object() const noexcept
    { return this->_object; }
    
    void setObject(Object* object) noexcept
    { this->_object = object; }
};

class ObjectBoundMethod : public Object
{
public:
    //! \brief constructor and desctructor
    ObjectBoundMethod(const Value& receiver, Object* method) noexcept;
    virtual ~ObjectBoundMethod() noexcept override {};

    //! \brief print object bound method
    virtual void print(bool callStr = true) const override;

private:
    Value _recevier;
    Object* _method;

public:
    const Value& recevier() const noexcept
    { return this->_recevier; }
    
    Value& recevier() noexcept
    { return this->_recevier; }

    Object* method() const noexcept
    { return this->_method; }
};

}

#endif